<html lang="en-US">

<head>
    
<title>深入理解 Go Slice - 花落雨忧</title>

<meta property="og:title" content="深入理解 Go Slice - 花落雨忧">



    



    
    <meta property="description" content="[&amp;hellip;] 在 Go 中，Slice（切片）是抽象在 Array（数组）之上的特殊类型。为了更好地了解 Slice，第一步需要先对 Array 进行理解。深刻了解 Slice 与 Array 之间的区别后，就能更好的对其底层一番摸索 😄
[&amp;hellip;] func main() { nums := [3]int{} nums[0] = 1 n := nums[0] n = 2 &amp;hellip;">
    <meta property="og:description" content="[&amp;hellip;] 在 Go 中，Slice（切片）是抽象在 Array（数组）之上的特殊类型。为了更好地了解 Slice，第一步需要先对 Array 进行理解。深刻了解 Slice 与 Array 之间的区别后，就能更好的对其底层一番摸索 😄
[&amp;hellip;] func main() { nums := [3]int{} nums[0] = 1 n := nums[0] n = 2 &amp;hellip;">
    






<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="https://www.lican.site/logo/logo.png" type="image/x-icon" />



<link rel="stylesheet" href="/css/style.min.css" />

<link rel="stylesheet" href="/css/reset.min.css" />




<script src="https://www.lican.site/js/highlight.min.js"></script>

<script>
hljs.configure({ ignoreUnescapedHTML: true })
hljs.highlightAll();
</script>


<script src="https://www.lican.site/js/jquery.min.js"></script>




<link href="https://www.lican.site/css/hugo-code.min.css" rel="stylesheet" />



    <style>
        .post-content img {
            max-width: 400px;
        }
    </style>
</head>

<body id="period" class="home blog">
    <a class="skip-content" href="#main">Press "Enter" to skip to content</a>
    <div id="overflow-container" class="overflow-container">
        <header class="site-header" id="site-header" role="banner">
    <div class="max-width">
        <div id="title-container" class="title-container">
            <div id="site-title" class="site-title"><a href="/">花落雨忧</a></div>
            <p class="tagline">思所及 力所行 方为真.</p>
        </div>
        
        <div id="menu-primary-container" class="menu-primary-container">
            <div class="icon-container">
            </div>
            <div id="menu-primary" class="menu-container menu-primary" role="navigation">
                <nav class="menu">
                    <ul id="menu-primary-items" class="menu-primary-items">
                        
                        
                        <li id="menu-item-0"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/posts" aria-current="page" tabindex="0">首页</a></li>
                        
                        <li id="menu-item-1"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/tech/" aria-current="page" tabindex="1">技术文档</a></li>
                        
                        <li id="menu-item-2"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/article/" aria-current="page" tabindex="2">文章</a></li>
                        
                        <li id="menu-item-3"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/project/" aria-current="page" tabindex="3">项目</a></li>
                        
                        <li id="menu-item-4"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/about/" aria-current="page" tabindex="4">关于</a></li>
                        
                    </ul>
                </nav>
            </div>
        </div>
    </div>
</header>
        <div id="primary-container" class="primary-container">
            <div class="max-width">
                <section id="main" class="main" role="main">
                    <div id="loop-container" class="loop-container">
                        <div
                            class="post type-post status-publish format-standard hentry entry">
                            <article>
                                <div class="post-container">
                                    <div class="post-header">
                                        <h2 class="post-title">
                                            <a href="/posts/go/slice/2018-12-11-slice/">深入理解 Go Slice</a>
                                        </h2>
                                        
                                        <div class="post-byline">Published on
                                            <a class="date" href="javascript:;">2018/12/11</a>
                                            
                                            
                                            
                                            By 
                                            <a class="author" href="javascript:;">煎鱼</a></div>
                                            
                                        
                                    </div>
                                    <div class="post-content">
                                        <p><img src="https://s2.ax1x.com/2020/02/27/3dXSeA.png" alt="image"></p>
<h2 id="是什么">是什么</h2>
<p>在 Go 中，Slice（切片）是抽象在 Array（数组）之上的特殊类型。为了更好地了解 Slice，第一步需要先对 Array 进行理解。深刻了解 Slice 与 Array 之间的区别后，就能更好的对其底层一番摸索 😄</p>
<h2 id="用法">用法</h2>
<h3 id="array">Array</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">n</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>]
	<span style="color:#a6e22e">n</span> = <span style="color:#ae81ff">2</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v\n&#34;</span>, <span style="color:#a6e22e">nums</span>)
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;n: %d\n&#34;</span>, <span style="color:#a6e22e">n</span>)
}
</code></pre></div><p>我们可得知在 Go 中，数组类型需要指定长度和元素类型。在上述代码中，可得知 <code>[3]int{}</code> 表示 3 个整数的数组，并进行了初始化。底层数据存储为一段连续的内存空间，通过固定的索引值（下标）进行检索</p>
<p><img src="https://s2.ax1x.com/2020/02/27/3dXASS.png" alt="image"></p>
<p>数组在声明后，其元素的初始值（也就是零值）为 0。并且该变量可以直接使用，不需要特殊操作</p>
<p>同时数组的长度是固定的，它的长度是类型的一部分，因此 <code>[3]int</code> 和 <code>[4]int</code> 在类型上是不同的，不能称为 “一个东西”</p>
<h4 id="输出结果">输出结果</h4>
<pre><code>nums: [1 0 0]
n: 2
</code></pre><h3 id="slice">Slice</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">dnums</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[:]

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dnums: %v&#34;</span>, <span style="color:#a6e22e">dnums</span>)
}
</code></pre></div><p>Slice 是对 Array 的抽象，类型为 <code>[]T</code>。在上述代码中，<code>dnums</code> 变量通过 <code>nums[:]</code> 进行赋值。需要注意的是，Slice 和 Array 不一样，它不需要指定长度。也更加的灵活，能够自动扩容</p>
<h2 id="数据结构">数据结构</h2>
<p><img src="https://s2.ax1x.com/2020/02/27/3wmr3F.png" alt="image"></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">slice</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">array</span> <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>
	<span style="color:#a6e22e">len</span>   <span style="color:#66d9ef">int</span>
	<span style="color:#a6e22e">cap</span>   <span style="color:#66d9ef">int</span>
}
</code></pre></div><p>Slice 的底层数据结构共分为三部分，如下：</p>
<ul>
<li>array：指向所引用的数组指针（<code>unsafe.Pointer</code> 可以表示任何可寻址的值的指针）</li>
<li>len：长度，当前引用切片的元素个数</li>
<li>cap：容量，当前引用切片的容量（底层数组的元素总数）</li>
</ul>
<p>在实际使用中，cap 一定是大于或等于 len 的。否则会导致 panic</p>
<h3 id="示例">示例</h3>
<p>为了更好的理解，我们回顾上小节的代码便于演示，如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">dnums</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[:]

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dnums: %v&#34;</span>, <span style="color:#a6e22e">dnums</span>)
}
</code></pre></div><p><img src="https://s2.ax1x.com/2020/02/27/3wmoge.png" alt="image"></p>
<p>在代码中，可观察到 <code>dnums := nums[:]</code>，这段代码确定了 Slice 的 Pointer 指向数组，且 len 和 cap 都为数组的基础属性。与图示表达一致</p>
<h3 id="lencap-不同">len、cap 不同</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">dnums</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>:<span style="color:#ae81ff">2</span>]

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dnums: %v, len: %d, cap: %d&#34;</span>, <span style="color:#a6e22e">dnums</span>, len(<span style="color:#a6e22e">dnums</span>), cap(<span style="color:#a6e22e">dnums</span>))
}
</code></pre></div><p><img src="https://s2.ax1x.com/2020/02/27/3wmxC8.png" alt="image"></p>
<h4 id="输出结果-1">输出结果</h4>
<pre><code>dnums: [1 0], len: 2, cap: 3
</code></pre><p>显然，在这里指定了 <code>Slice[0:2]</code>，因此 len 为所引用元素的个数，cap 为所引用的数组元素总个数。与期待一致 😄</p>
<h2 id="创建">创建</h2>
<p>Slice 的创建有两种方式，如下：</p>
<ul>
<li><code>var []T</code> 或 <code>[]T{}</code></li>
<li><code>func make（[] T，len，cap）[] T</code></li>
</ul>
<p>可以留意 make 函数，我们都知道 Slice 需要指向一个 Array。那 make 是怎么做的呢？</p>
<p>它会在调用 make 的时候，分配一个数组并返回引用该数组的 Slice</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">makeslice</span>(<span style="color:#a6e22e">et</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">_type</span>, <span style="color:#a6e22e">len</span>, <span style="color:#a6e22e">cap</span> <span style="color:#66d9ef">int</span>) <span style="color:#a6e22e">slice</span> {
	<span style="color:#a6e22e">maxElements</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">maxSliceCap</span>(<span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">size</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">len</span> &lt; <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> uintptr(<span style="color:#a6e22e">len</span>) &gt; <span style="color:#a6e22e">maxElements</span> {
		panic(<span style="color:#a6e22e">errorString</span>(<span style="color:#e6db74">&#34;makeslice: len out of range&#34;</span>))
	}

	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">cap</span> &lt; <span style="color:#a6e22e">len</span> <span style="color:#f92672">||</span> uintptr(<span style="color:#a6e22e">cap</span>) &gt; <span style="color:#a6e22e">maxElements</span> {
		panic(<span style="color:#a6e22e">errorString</span>(<span style="color:#e6db74">&#34;makeslice: cap out of range&#34;</span>))
	}

	<span style="color:#a6e22e">p</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">mallocgc</span>(<span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">size</span><span style="color:#f92672">*</span>uintptr(<span style="color:#a6e22e">cap</span>), <span style="color:#a6e22e">et</span>, <span style="color:#66d9ef">true</span>)
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">slice</span>{<span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">len</span>, <span style="color:#a6e22e">cap</span>}
}
</code></pre></div><ul>
<li>根据传入的 Slice 类型，获取其类型能够申请的最大容量大小</li>
<li>判断 len 是否合规，检查是否在 0 &lt; x &lt; maxElements 范围内</li>
<li>判断 cap 是否合规，检查是否在 len &lt; x &lt; maxElements 范围内</li>
<li>申请 Slice 所需的内存空间对象。若为大型对象（大于 32 KB）则直接从堆中分配</li>
<li>返回申请成功的 Slice 内存地址和相关属性（默认返回申请到的内存起始地址）</li>
</ul>
<h2 id="扩容">扩容</h2>
<p>当使用 Slice 时，若存储的元素不断增长（例如通过 append）。当条件满足扩容的策略时，将会触发自动扩容</p>
<p>那么分别是什么规则呢？让我们一起看看源码是怎么说的 😄</p>
<h3 id="zerobase">zerobase</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">growslice</span>(<span style="color:#a6e22e">et</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">_type</span>, <span style="color:#a6e22e">old</span> <span style="color:#a6e22e">slice</span>, <span style="color:#a6e22e">cap</span> <span style="color:#66d9ef">int</span>) <span style="color:#a6e22e">slice</span> {
	<span style="color:#f92672">...</span>
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">size</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">cap</span> &lt; <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">cap</span> {
			panic(<span style="color:#a6e22e">errorString</span>(<span style="color:#e6db74">&#34;growslice: cap out of range&#34;</span>))
		}

		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">slice</span>{<span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">zerobase</span>), <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">len</span>, <span style="color:#a6e22e">cap</span>}
	}
    <span style="color:#f92672">...</span>
}
</code></pre></div><p>当 Slice size 为 0 时，若将要扩容的容量比原本的容量小，则抛出异常（也就是不支持缩容操作）。否则，将重新生成一个新的 Slice 返回，其 Pointer 指向一个 0 byte 地址（不会保留老的 Array 指向）</p>
<h3 id="扩容---计算策略">扩容 - 计算策略</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">growslice</span>(<span style="color:#a6e22e">et</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">_type</span>, <span style="color:#a6e22e">old</span> <span style="color:#a6e22e">slice</span>, <span style="color:#a6e22e">cap</span> <span style="color:#66d9ef">int</span>) <span style="color:#a6e22e">slice</span> {
    <span style="color:#f92672">...</span>
    <span style="color:#a6e22e">newcap</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">cap</span>
	<span style="color:#a6e22e">doublecap</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">newcap</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">newcap</span>
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">cap</span> &gt; <span style="color:#a6e22e">doublecap</span> {
		<span style="color:#a6e22e">newcap</span> = <span style="color:#a6e22e">cap</span>
	} <span style="color:#66d9ef">else</span> {
		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">len</span> &lt; <span style="color:#ae81ff">1024</span> {
			<span style="color:#a6e22e">newcap</span> = <span style="color:#a6e22e">doublecap</span>
		} <span style="color:#66d9ef">else</span> {
			<span style="color:#66d9ef">for</span> <span style="color:#ae81ff">0</span> &lt; <span style="color:#a6e22e">newcap</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">newcap</span> &lt; <span style="color:#a6e22e">cap</span> {
				<span style="color:#a6e22e">newcap</span> <span style="color:#f92672">+=</span> <span style="color:#a6e22e">newcap</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">4</span>
			}
			<span style="color:#f92672">...</span>
		}
	}
	<span style="color:#f92672">...</span>
}
</code></pre></div><ul>
<li>若 Slice cap 大于 doublecap，则扩容后容量大小为 新 Slice 的容量（超了基准值，我就只给你需要的容量大小）</li>
<li>若 Slice len 小于 1024 个，在扩容时，增长因子为 1（也就是 3 个变 6 个）</li>
<li>若 Slice len 大于 1024 个，在扩容时，增长因子为 0.25（原本容量的四分之一）</li>
</ul>
<p>注：也就是小于 1024 个时，增长 2 倍。大于 1024 个时，增长 1.25 倍</p>
<h3 id="扩容---内存策略">扩容 - 内存策略</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">growslice</span>(<span style="color:#a6e22e">et</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">_type</span>, <span style="color:#a6e22e">old</span> <span style="color:#a6e22e">slice</span>, <span style="color:#a6e22e">cap</span> <span style="color:#66d9ef">int</span>) <span style="color:#a6e22e">slice</span> {
    <span style="color:#f92672">...</span>
    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">overflow</span> <span style="color:#66d9ef">bool</span>
	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">lenmem</span>, <span style="color:#a6e22e">newlenmem</span>, <span style="color:#a6e22e">capmem</span> <span style="color:#66d9ef">uintptr</span>
	<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ptrSize</span> = <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Sizeof</span>((<span style="color:#f92672">*</span><span style="color:#66d9ef">byte</span>)(<span style="color:#66d9ef">nil</span>))
	<span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">size</span> {
	<span style="color:#66d9ef">case</span> <span style="color:#ae81ff">1</span>:
		<span style="color:#a6e22e">lenmem</span> = uintptr(<span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">len</span>)
		<span style="color:#a6e22e">newlenmem</span> = uintptr(<span style="color:#a6e22e">cap</span>)
		<span style="color:#a6e22e">capmem</span> = <span style="color:#a6e22e">roundupsize</span>(uintptr(<span style="color:#a6e22e">newcap</span>))
		<span style="color:#a6e22e">overflow</span> = uintptr(<span style="color:#a6e22e">newcap</span>) &gt; <span style="color:#a6e22e">_MaxMem</span>
		<span style="color:#a6e22e">newcap</span> = int(<span style="color:#a6e22e">capmem</span>)
	    <span style="color:#f92672">...</span>
	}

	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">cap</span> &lt; <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">cap</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">overflow</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">capmem</span> &gt; <span style="color:#a6e22e">_MaxMem</span> {
		panic(<span style="color:#a6e22e">errorString</span>(<span style="color:#e6db74">&#34;growslice: cap out of range&#34;</span>))
	}

	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">kind</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">kindNoPointers</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
		<span style="color:#a6e22e">p</span> = <span style="color:#a6e22e">mallocgc</span>(<span style="color:#a6e22e">capmem</span>, <span style="color:#66d9ef">nil</span>, <span style="color:#66d9ef">false</span>)
		<span style="color:#a6e22e">memmove</span>(<span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">array</span>, <span style="color:#a6e22e">lenmem</span>)
		<span style="color:#a6e22e">memclrNoHeapPointers</span>(<span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">newlenmem</span>), <span style="color:#a6e22e">capmem</span><span style="color:#f92672">-</span><span style="color:#a6e22e">newlenmem</span>)
	} <span style="color:#66d9ef">else</span> {
		<span style="color:#a6e22e">p</span> = <span style="color:#a6e22e">mallocgc</span>(<span style="color:#a6e22e">capmem</span>, <span style="color:#a6e22e">et</span>, <span style="color:#66d9ef">true</span>)
		<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">writeBarrier</span>.<span style="color:#a6e22e">enabled</span> {
			<span style="color:#a6e22e">memmove</span>(<span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">array</span>, <span style="color:#a6e22e">lenmem</span>)
		} <span style="color:#66d9ef">else</span> {
			<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> uintptr(<span style="color:#ae81ff">0</span>); <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">lenmem</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">+=</span> <span style="color:#a6e22e">et</span>.<span style="color:#a6e22e">size</span> {
				<span style="color:#a6e22e">typedmemmove</span>(<span style="color:#a6e22e">et</span>, <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">i</span>), <span style="color:#a6e22e">add</span>(<span style="color:#a6e22e">old</span>.<span style="color:#a6e22e">array</span>, <span style="color:#a6e22e">i</span>))
			}
		}
	}
	<span style="color:#f92672">...</span>
}
</code></pre></div><p>1、获取老 Slice 长度和计算假定扩容后的新 Slice 元素长度、容量大小以及指针地址（用于后续操作内存的一系列操作）</p>
<p>2、确定新 Slice 容量大于老 Sice，并且新容量内存小于指定的最大内存、没有溢出。否则抛出异常</p>
<p>3、若元素类型为 <code>kindNoPointers</code>，也就是<strong>非指针</strong>类型。则在老 Slice 后继续扩容</p>
<ul>
<li>第一步：根据先前计算的 <code>capmem</code>，在老 Slice cap 后继续申请内存空间，其后用于扩容</li>
<li>第二步：将 old.array 上的 n 个 bytes（根据 lenmem）拷贝到新的内存空间上</li>
<li>第三步：新内存空间（p）加上新 Slice cap 的容量地址。最终得到完整的新 Slice cap 内存地址 <code>add(p, newlenmem)</code> （ptr）</li>
<li>第四步：从 ptr 开始重新初始化 n 个 bytes（capmem-newlenmem）</li>
</ul>
<p>注：那么问题来了，为什么要重新初始化这块内存呢？这是因为 ptr 是未初始化的内存（例如：可重用的内存，一般用于新的内存分配），其可能包含 “垃圾”。因此在这里应当进行 “清理”。便于后面实际使用（扩容）</p>
<p>4、不满足 3 的情况下，重新申请并初始化一块内存给新 Slice 用于存储 Array</p>
<p>5、检测当前是否正在执行 GC，也就是当前是否启用 Write Barrier（写屏障），若<strong>启用</strong>则通过 <code>typedmemmove</code> 方法，利用指针运算<strong>循环拷贝</strong>。否则通过 <code>memmove</code> 方法采取<strong>整体拷贝</strong>的方式将 lenmem 个字节从 old.array 拷贝到 ptr，以此达到更高的效率</p>
<p>注：一般会在 GC 标记阶段启用 Write Barrier，并且 Write Barrier 只针对指针启用。那么在第 5 点中，你就不难理解为什么会有两种截然不同的处理方式了</p>
<h4 id="小结">小结</h4>
<p>这里需要注意的是，扩容时的内存管理的选择项，如下：</p>
<ul>
<li>翻新扩展：当前元素为 <code>kindNoPointers</code>，将在老 Slice cap 的地址后继续申请空间用于扩容</li>
<li>举家搬迁：重新申请一块内存地址，整体迁移并扩容</li>
</ul>
<h3 id="两个小-陷阱">两个小 “陷阱”</h3>
<h4 id="一同根">一、同根</h4>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v , len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">nums</span>, len(<span style="color:#a6e22e">nums</span>), cap(<span style="color:#a6e22e">nums</span>))

	<span style="color:#a6e22e">dnums</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>:<span style="color:#ae81ff">2</span>]
	<span style="color:#a6e22e">dnums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">5</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v ,len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">nums</span>, len(<span style="color:#a6e22e">nums</span>), cap(<span style="color:#a6e22e">nums</span>))
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dnums: %v, len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">dnums</span>, len(<span style="color:#a6e22e">dnums</span>), cap(<span style="color:#a6e22e">dnums</span>))
}
</code></pre></div><p>输出结果：</p>
<pre><code>nums: [1 0 0] , len: 3, cap: 3
nums: [5 0 0] ,len: 3, cap: 3
dnums: [5 0], len: 2, cap: 3
</code></pre><p>在<strong>未扩容前</strong>，Slice array 指向所引用的 Array。因此在 Slice 上的变更。会直接修改到原始 Array 上（两者所引用的是同一个）</p>
<p><img src="https://s2.ax1x.com/2020/02/27/3wnibn.png" alt="image"></p>
<h4 id="二时过境迁">二、时过境迁</h4>
<p>随着 Slice 不断 append，内在的元素越来越多，终于触发了扩容。如下代码：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> [<span style="color:#ae81ff">3</span>]<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v , len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">nums</span>, len(<span style="color:#a6e22e">nums</span>), cap(<span style="color:#a6e22e">nums</span>))

	<span style="color:#a6e22e">dnums</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">nums</span>[<span style="color:#ae81ff">0</span>:<span style="color:#ae81ff">2</span>]
	<span style="color:#a6e22e">dnums</span> = append(<span style="color:#a6e22e">dnums</span>, []<span style="color:#66d9ef">int</span>{<span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>}<span style="color:#f92672">...</span>)
	<span style="color:#a6e22e">dnums</span>[<span style="color:#ae81ff">1</span>] = <span style="color:#ae81ff">1</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v ,len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">nums</span>, len(<span style="color:#a6e22e">nums</span>), cap(<span style="color:#a6e22e">nums</span>))
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dnums: %v, len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">dnums</span>, len(<span style="color:#a6e22e">dnums</span>), cap(<span style="color:#a6e22e">dnums</span>))
}
</code></pre></div><p>输出结果：</p>
<pre><code>nums: [1 0 0] , len: 3, cap: 3
nums: [1 0 0] ,len: 3, cap: 3
dnums: [1 1 2 3], len: 4, cap: 6
</code></pre><p>往 Slice append 元素时，若满足扩容策略，也就是假设插入后，原本数组的容量就超过最大值了</p>
<p>这时候内部就会重新申请一块内存空间，将原本的元素<strong>拷贝</strong>一份到新的内存空间上。此时其与原本的数组就没有任何关联关系了，<strong>再进行修改值也不会变动到原始数组</strong>。这是需要注意的</p>
<p><img src="https://s2.ax1x.com/2020/02/27/3wnAU0.png" alt="image"></p>
<h2 id="复制">复制</h2>
<h3 id="原型">原型</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">copy</span><span style="color:#960050;background-color:#1e0010">（</span><span style="color:#a6e22e">dst</span><span style="color:#960050;background-color:#1e0010">，</span><span style="color:#a6e22e">src</span> [] <span style="color:#a6e22e">T</span><span style="color:#960050;background-color:#1e0010">）</span><span style="color:#66d9ef">int</span>
</code></pre></div><p>copy 函数将数据从<strong>源 Slice</strong>复制到<strong>目标 Slice</strong>。它返回复制的元素数。</p>
<h3 id="示例-1">示例</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">dst</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">int</span>{<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>, <span style="color:#ae81ff">3</span>}
	<span style="color:#a6e22e">src</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">int</span>{<span style="color:#ae81ff">4</span>, <span style="color:#ae81ff">5</span>, <span style="color:#ae81ff">6</span>, <span style="color:#ae81ff">7</span>, <span style="color:#ae81ff">8</span>}
	<span style="color:#a6e22e">n</span> <span style="color:#f92672">:=</span> copy(<span style="color:#a6e22e">dst</span>, <span style="color:#a6e22e">src</span>)

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;dst: %v, n: %d&#34;</span>, <span style="color:#a6e22e">dst</span>, <span style="color:#a6e22e">n</span>)
}
</code></pre></div><p>copy 函数支持在不同长度的 Slice 之间进行复制，若出现长度不一致，在复制时会按照最少的 Slice 元素个数进行复制</p>
<p>那么在源码中是如何完成复制这一个行为的呢？我们来一起看看源码的实现，如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">slicecopy</span>(<span style="color:#a6e22e">to</span>, <span style="color:#a6e22e">fm</span> <span style="color:#a6e22e">slice</span>, <span style="color:#a6e22e">width</span> <span style="color:#66d9ef">uintptr</span>) <span style="color:#66d9ef">int</span> {
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">fm</span>.<span style="color:#a6e22e">len</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">to</span>.<span style="color:#a6e22e">len</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
		<span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>
	}

	<span style="color:#a6e22e">n</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fm</span>.<span style="color:#a6e22e">len</span>
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">to</span>.<span style="color:#a6e22e">len</span> &lt; <span style="color:#a6e22e">n</span> {
		<span style="color:#a6e22e">n</span> = <span style="color:#a6e22e">to</span>.<span style="color:#a6e22e">len</span>
	}

	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">width</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">n</span>
	}

	<span style="color:#f92672">...</span>

	<span style="color:#a6e22e">size</span> <span style="color:#f92672">:=</span> uintptr(<span style="color:#a6e22e">n</span>) <span style="color:#f92672">*</span> <span style="color:#a6e22e">width</span>
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">size</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span> {
		<span style="color:#f92672">*</span>(<span style="color:#f92672">*</span><span style="color:#66d9ef">byte</span>)(<span style="color:#a6e22e">to</span>.<span style="color:#a6e22e">array</span>) = <span style="color:#f92672">*</span>(<span style="color:#f92672">*</span><span style="color:#66d9ef">byte</span>)(<span style="color:#a6e22e">fm</span>.<span style="color:#a6e22e">array</span>) <span style="color:#75715e">// known to be a byte pointer
</span><span style="color:#75715e"></span>	} <span style="color:#66d9ef">else</span> {
		<span style="color:#a6e22e">memmove</span>(<span style="color:#a6e22e">to</span>.<span style="color:#a6e22e">array</span>, <span style="color:#a6e22e">fm</span>.<span style="color:#a6e22e">array</span>, <span style="color:#a6e22e">size</span>)
	}
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">n</span>
}
</code></pre></div><ul>
<li>若源 Slice 或目标 Slice 存在长度为 0 的情况，则直接返回 0（因为压根不需要执行复制行为）</li>
<li>通过对比两个 Slice，获取最小的 Slice 长度。便于后续操作</li>
<li>若 Slice 只有一个元素，则直接利用指针的特性进行转换</li>
<li>若 Slice 大于一个元素，则从 <code>fm.array</code> 复制 <code>size</code> 个字节到 <code>to.array</code> 的地址处（会覆盖原有的值）</li>
</ul>
<h2 id="奇特的初始化">&ldquo;奇特&quot;的初始化</h2>
<p>在 Slice 中流传着两个传说，分别是 Empty 和 Nil Slice，接下来让我们看看它们的小区别 🤓</p>
<h3 id="empty">Empty</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">nums</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">int</span>{}
	<span style="color:#a6e22e">renums</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">0</span>)

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;nums: %v, len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">nums</span>, len(<span style="color:#a6e22e">nums</span>), cap(<span style="color:#a6e22e">nums</span>))
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;renums: %v, len: %d, cap: %d\n&#34;</span>, <span style="color:#a6e22e">renums</span>, len(<span style="color:#a6e22e">renums</span>), cap(<span style="color:#a6e22e">renums</span>))
}
</code></pre></div><p>输出结果：</p>
<pre><code>nums: [], len: 0, cap: 0
renums: [], len: 0, cap: 0
</code></pre><h3 id="nil">Nil</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">nums</span> []<span style="color:#66d9ef">int</span>
}
</code></pre></div><p>输出结果：</p>
<pre><code>nums: [], len: 0, cap: 0
</code></pre><h3 id="想一想">想一想</h3>
<p>乍一看，Empty Slice 和 Nil Slice 好像一模一样？不管是 len，还是 cap 都为 0。好像没区别？我们再看看如下代码：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">nums</span> []<span style="color:#66d9ef">int</span>
	<span style="color:#a6e22e">renums</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">0</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">nums</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;nums is nil.&#34;</span>)
	}
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">renums</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;renums is nil.&#34;</span>)
	}
}
</code></pre></div><p>你觉得输出结果是什么呢？你可能已经想到了，最终的输出结果：</p>
<pre><code>nums is nil.
</code></pre><h4 id="为什么">为什么</h4>
<h5 id="empty-1">Empty</h5>
<p><img src="https://s2.ax1x.com/2020/02/27/3wncRS.png" alt="image"></p>
<h5 id="nil-1">Nil</h5>
<p><img src="https://s2.ax1x.com/2020/02/27/3wn5aq.png" alt="image"></p>
<p>从图示中可以看出来，两者有本质上的区别。其底层数组的指向指针是不一样的，Nil Slice 指向的是 nil，Empty Slice 指向的是实际存在的空数组地址</p>
<p>你可以认为，Nil Slice 代指不存在的 Slice，Empty Slice 代指空集合。两者所代表的意义是完全不同的</p>
<h2 id="总结">总结</h2>
<p>通过本文，可得知 Go Slice 相当灵活。不需要你手动扩容，也不需要你关注加多少减多少。对 Array 是动态引用，是 Go 类型的一个极大的补充，也因此在应用中使用的更多、更便捷</p>
<p>虽然有个别要注意的 “坑”，但其实是合理的。你觉得呢？😄</p>


                                        
                                        
                                        
                                        <div class="rp4wp-related-posts">
                                            <h3>相关文章</h3>
                                            <ul>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/pkg/2018-12-04-fmt/">fmt 标准库 --- Print* 是怎么样输出的？</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/pkg/2018-09-28-log/">log 标准库</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/talk/2018-11-25-gomock/">使用 Gomock 进行单元测试</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/grpc/2018-10-20-zipkin/">「连载十」分布式链路追踪 gRPC &#43; Opentracing &#43; Zipkin</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/grpc/2018-10-16-deadlines/">「连载九」gRPC Deadlines</a>
                                                    </div>
                                                </li>
                                                
                                            </ul>
                                        </div>
                                        
                                        
                                    </div>

                                    
                                    
                                    

                                    
                                    <div class="post-meta">
                                        
                                        
                                        <div class="post-tags">
                                            <ul>
                                            
                                            <li>
                                                <a href="/tags/go" title="View all posts tagged match">go</a>
                                            </li>
                                            
                                            <li>
                                                <a href="/tags/%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90" title="View all posts tagged match">源码分析</a>
                                            </li>
                                            
                                            </ul>
                                        </div>
                                        
                                        
                                        <nav class="further-reading">
                                            
                                            <div class="previous">
                                                <span>&lt;&lt; Prev</span>
                                                <a href="https://www.lican.site/posts/go/pkg/2018-12-04-fmt/"
                                                    rel="prev">fmt 标准库 --- Print* 是怎么样输出的？</a> </div>
                                            
                                            
                                            <div class="next">
                                                <span>Next >></span>
                                                <a href="https://www.lican.site/posts/go/pkg/2018-12-15-unsafe/">有点不安全却又一亮的 Go unsafe.Pointer</a> 
                                            </div>
                                            
                                        </nav>
                                    </div>
                                    
                                    

                                    
                                    
                                    

                                </div>
                            </article>
                        </div>
                    </div>
                </section>
                <aside class="sidebar sidebar-primary" id="sidebar-primary" role="complementary">
    <h1 class="screen-reader-text">Sidebar</h1>
    

    
    
    <section id="text-2" class="widget widget_text">
        <div class="textwidget">
            
            <div id="profile">
                <div id="profile_picture"><img src="https://www.lican.site/logo/logo.png"></div>
                <div id="profile_intro">
                    <p><span class="name">Lican</span></p>
                    <p class="intro">全栈开发者，爱好造轮子。</p>
                </div>
            </div>
            
            <p>
                <script type="text/javascript">
                    (function ($) {
                        $(document).ready(function () {
                            var menuPrimaryContainer = $('#menu-primary-container');
                            var profile = $('#text-2');
                            $('#toggle-navigation').click(function () {
                                if (menuPrimaryContainer.hasClass('open')) {
                                    profile.removeClass('open');
                                } else {
                                    profile.addClass('open');
                                }
                            });
                        });
                    })(jQuery);
                </script>
            </p>
        </div>
    </section>
    
    
    
    
    
    <section id="text-5" class="widget widget_text">
        <h2 class="widget-title">开源项目</h2>
        <div class="textwidget">
            <div id="projects" style="line-height: 22px;">
                
                <a href="https://github.com/idoubi/gonews"
                    target="_blank">gonews</a>: &nbsp;Daily news for golang<br>
                
                <a href="https://github.com/idoubi/sql2struct"
                    target="_blank">sql2struct</a>: &nbsp;Generate go struct according to SQL<br>
                
                <a href="https://github.com/idoubi/goz"
                    target="_blank">goz</a>: &nbsp;Request library used in golang<br>
                
        </div>
    </section>
    
    

    
    
    
    
    

    
    
    

    
    
    

    
    
    
    
</aside>
            </div>
        </div>

        <footer id="site-footer" class="site-footer" role="contentinfo">
    <div class="max-width">
    </div>
    <div class="footer">
        <div id="footercontent">
            © lican.vip All rights reserved<br/>
            Built with Hugo Theme <a href="https://github.com/idoubi/hugo-theme-period" target="_blank">Period</a>
        </div>
    </div>
</footer>

<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e8351b6d4626d5881d439ea1f6184baa";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>
    
    
    </div>
    
</body>

</html>